package mk.jug.academy.service;

import mk.jug.academy.exception.BusinessException;
import mk.jug.academy.model.Page;
import mk.jug.academy.model.Sponsor;
import mk.jug.academy.model.SponsorStatus;
import mk.jug.academy.preconditions.ErrorCodes;
import mk.jug.academy.testing.bdd.IntegrationBehavior;
import mk.jug.academy.testing.factory.SponsorTestFactory;
import mk.jug.academy.testing.util.TestHelper;

import org.springframework.beans.factory.annotation.Autowired;

import com.googlecode.instinct.marker.annotate.Specification;
import com.googlecode.instinct.marker.annotate.Specification.SpecificationState;
import com.googlecode.instinct.marker.annotate.Subject;

/**
 * An integration test unit targeting the behavior of a {@link Sponsor} within
 * the JUGMK Academy. 
 *
 * @author Ice.Penov
 */
public class SponsorLifecycle extends IntegrationBehavior {
	
	@Autowired private SponsorService sponsorService;
	
	@Subject private Sponsor sponsor;
	@Subject private Sponsor sponsor1;
	@Subject private Sponsor sponsor2;
	@Subject private Sponsor sponsor3;
	
	@Override
	protected void beforeHook() {
		sponsor = SponsorTestFactory.createSponsor();
		sponsor1 = SponsorTestFactory.createSponsor();
		sponsor2 = SponsorTestFactory.createSponsor();
		sponsor3 = SponsorTestFactory.createSponsor();
		TestHelper.init(sponsorService);
	}
	
	/**
	 * User action:
	 * A {@link Sponsor} with already registered email tries to register.
	 * 
	 * Expected behavior:
	 * 1. Throw an {@link BusinessException} with the appropriate error code.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void failToRegisterSponsorWithExistingEmail() {
		sponsorService.register(sponsor);
		new ExceptionExpectation() {
			public void execute() {
				sponsorService.register(sponsor);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_EMAIL_EXISTS);
	}
	
	/**
	 * User action:
	 * A {@link Sponsor} registers to the Java Academy.
	 * 
	 * Expected behavior:
	 * 1. The {@link Sponsor} should be persisted
	 * 2. Should be listed as a {@link SponsorStatus#REGISTERED} sponsor.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void registerSponsor() {
		sponsorService.register(sponsor);
		//should be the only one registered course.
		expect.that(sponsorService.listRegistered(1, 2).getItems()).containsItem(sponsor);
		//should not list in the other states (ACTIVE,DELETED)
		expect.that(sponsorService.listActive(1, 2).getItems()).isEmpty();
		expect.that(sponsorService.listDeleted(1, 2).getItems()).isEmpty();		
	}
	
	/**
	 * User action:
	 * A {@link Sponsor} is being activated.
	 * 
	 * Expected behavior:
	 * 1. The {@link Sponsor} should be listed as {@link SponsorStatus#ACTIVE}
	 * 2. The same should not be listed in other states
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void activateSponsor() {
		sponsorService.register(sponsor);
		sponsorService.activate(sponsor);
		//expect it in the ACTIVE list
		expect.that(sponsorService.listActive(1, 2).getItems()).containsItem(sponsor);
		expect.that(sponsorService.listActive(1, 2).getItems()).isOfSize(1);
		//and not in the other lists
		expect.that(sponsorService.listRegistered(1, 2).getItems()).isEmpty();
		expect.that(sponsorService.listDeleted(1, 2).getItems()).isEmpty();
	}
	
	/**
	 * User action:
	 * A {@link Sponsor} is being deleted (soft).
	 * 
	 * Expected behavior:
	 * 1. The {@link Sponsor} should be listed as {@link SponsorStatus#DELETED}
	 * 2. The same should not be listed in other states
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void deleteSponsor() {
		sponsorService.register(sponsor);
		sponsorService.activate(sponsor);
		sponsorService.delete(sponsor);
		//expect it in the DELETE list
		expect.that(sponsorService.listDeleted(1, 2).getItems()).containsItem(sponsor);
		expect.that(sponsorService.listDeleted(1, 2).getItems()).isOfSize(1);
		//and not in the other lists
		expect.that(sponsorService.listActive(1, 2).getItems()).isEmpty();
		expect.that(sponsorService.listRegistered(1, 2).getItems()).isEmpty();		
	}
	
	@Specification(state = SpecificationState.COMPLETE)
	public void findActiveSponsorByEmail() {
		TestHelper.prepareActiveSponsor(sponsor);
		expect.that(sponsorService.findByEmail(sponsor.getEmail())).isEqualTo(sponsor);
	}
	
	/**
	 * User action:
	 * Fetch the sponsors in a paged manner.
	 * 
	 * Expected behavior:
	 * List the records as expected.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void navigateSponsorsUsingPaging() {
		sponsor1.setRating(1);
		sponsor2.setRating(2);
		sponsor3.setRating(3);
		TestHelper.prepareActiveSponsor(sponsor1, sponsor2, sponsor3);
		//open the pages.
		Page<Sponsor> page1 = sponsorService.listActive(1, 2);
		Page<Sponsor> page2 = sponsorService.listActive(2, 2);
		Page<Sponsor> page3 = sponsorService.listActive(3, 2);
		//expectations.
		//size:
		expect.that(page1.getTotalCount()).isEqualTo(3);
		expect.that(page2.getTotalCount()).isEqualTo(3);
		expect.that(page3.getTotalCount()).isEqualTo(3);
		expect.that(page1.getItems()).isOfSize(2);
		expect.that(page2.getItems()).isOfSize(1);
		expect.that(page3.getItems()).isEmpty();
		//check contents of the pages:
		expect.that(page1.getItems()).containsItems(sponsor3, sponsor2);
		expect.that(page2.getItems()).containsItem(sponsor1);
	}
	
}